﻿using Caffeine.Core.Test.TestModel.Merge;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Caffeine.Core.Test
{
    [TestClass]
    public class MergeTester
    {
        [TestMethod]
        public void TestMergeWithNoProperties()
        {
            ModelFactory factory = new ModelFactory();
            ModelA modelA = factory.CreateModel<ModelA>();
            modelA.Merge(modelA);
            modelA.Merge(factory.CreateModel<ModelA>());
        }

        [TestMethod]
        public void TestMergeWithPrimitiveProperties()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelC modelC1 = factory.CreateModel<ModelC>();
            ModelC modelC2 = factory.CreateModel<ModelC>();

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);

            modelC1.Merge(modelC2);

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);

            modelC1.Merge(modelC1);

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);

            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            Assert.IsFalse(modelD1.IsDirty);
            Assert.IsFalse(modelD1.IsDirtyWithin);
            Assert.IsFalse(modelD2.IsDirty);
            Assert.IsFalse(modelD2.IsDirtyWithin);

            modelD1.Merge(modelD2);

            Assert.IsFalse(modelD1.IsDirty);
            Assert.IsFalse(modelD1.IsDirtyWithin);
            Assert.IsFalse(modelD2.IsDirty);
            Assert.IsFalse(modelD2.IsDirtyWithin);

            modelD2.Merge(modelD2);

            Assert.IsFalse(modelD2.IsDirty);
            Assert.IsFalse(modelD2.IsDirtyWithin);

            AssertValuesAreTheSame(modelC1, modelC2);

            modelC1.IntProperty = 2;
            modelC1.CharProperty = 'A';
            modelC1.DecimalProperty = 123M;
            modelC1.NullableIntProperty = 5;
            modelC1.NullableCharProperty = 'B';
            modelC1.NullableDecimalProperty = 456M;
            modelC1.ObjectProperty = "Some Object";
            modelC1.StringProperty = "StringValue";
            modelC1.GuidProperty = Guid.NewGuid();
            modelC1.NullableGuidProperty = Guid.NewGuid();
            modelC1.TestEnumProperty = TestEnum.ValueB;
            modelC1.NullableTestEnumProperty = TestEnum.ValueC;
            modelC1.FirstName = "John";
            modelC1.LastName = "Doe";
            modelC1.CommitGraph();

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC1);

            modelC1.Merge(modelC1);

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);

            modelC1.Merge(modelC2);

            Assert.IsTrue(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC1.ResetGraph();

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);

            modelC2.Merge(modelC1);

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsTrue(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);
            AssertValuesAreTheSame(modelC2, modelC1);

            modelC2.Commit();
            
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);

            modelC2.Merge(modelC1);

            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);
            AssertValuesAreTheSame(modelC2, modelC1);

            modelC2.ModelCProperty = factory.CreateModel<ModelC>();
            modelC2.CommitGraph();
            modelC1.Merge(modelC2);

            Assert.IsTrue(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC2.ModelCProperty.IntProperty = 4;
            modelC2.ModelCProperty.ModelCProperty = factory.CreateModel<ModelC>();
            modelC2.ModelCProperty.ModelCProperty.StringProperty = "String Value";
            modelC2.ModelDProperty = factory.CreateModel<ModelD>();
            modelC2.ModelDProperty.GuidProperty = Guid.NewGuid();
            modelC2.ModelDProperty.IntProperty = 34;
            modelC2.ModelDProperty.IntProperty1 = 56;
            modelC2.FirstAndLastName.Value = "Robinson, Jerry";
            modelC1.Merge(modelC2);

            Assert.IsTrue(modelC1.IsDirty);
            Assert.IsTrue(modelC1.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);

            ModelC copiedModel = modelC1.ParentFactory.CreateDeepCopy(modelC1, GetTypesForSerialization());
            modelC1.Merge(copiedModel);
            AssertValuesAreTheSame(modelC1, copiedModel);

            copiedModel.ObjectProperty = 123;
            copiedModel.NullableTestEnumProperty = null;
            modelC1.Merge(copiedModel);
            AssertValuesAreTheSame(modelC1, copiedModel);
        }

        [TestMethod]
        public void TestMergeWithCollections()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelC modelC1 = factory.CreateModel<ModelC>();
            ModelC modelC2 = factory.CreateModel<ModelC>();

            modelC2.ModelCCollection.Add(factory.CreateModel<ModelC>());
            modelC2.ModelCCollection[0].StringProperty = "StringValue";
            modelC2.ModelCCollection[0].DecimalProperty = 54345M;
            
            modelC2.ModelCCollection.Add(factory.CreateModel<ModelC>());
            modelC2.ModelCCollection[1].IntProperty = 123;
            modelC2.ModelCCollection[1].GuidProperty = Guid.NewGuid();

            modelC1.Merge(modelC2);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC2.ModelCCollection[0].IntProperty = 123;
            modelC2.ModelCCollection[1].GuidProperty = Guid.NewGuid();
            modelC2.ModelCCollection[1].ModelDCollection.Add(factory.CreateModel<ModelD>());
            modelC2.ModelCCollection[1].ModelDCollection.Add(factory.CreateModel<ModelD>());
            modelC1.Merge(modelC2);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC1.CommitGraph();
            modelC2.CommitGraph();
            modelC2.ModelCCollection[1].ModelDCollection.Clear();
            modelC2.ModelCCollection[1].ModelDCollection.Add(factory.CreateModel<ModelD>());
            modelC2.ModelCCollection[1].ModelDCollection.Add(factory.CreateModel<ModelD>());
            modelC1.Merge(modelC2);
            AssertValuesAreTheSame(modelC1, modelC2);
        }

        [TestMethod]
        public void TestMergeWithDerivedTypes()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            ModelD childModel = factory.CreateModel<ModelD>();
            childModel.IntProperty = 789;
            childModel.GuidProperty = Guid.NewGuid();
            childModel.StringProperty1 = "StringValue";
            childModel.TestEnumProperty1 = TestEnum.ValueC;
            modelD2.ModelCProperty1 = childModel;
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            childModel.NullableDecimalProperty = 5;
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            childModel.UniqueId = Guid.NewGuid();
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            modelD2.ModelCProperty1 = null;
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            childModel = factory.CreateModel<ModelD>();
            childModel.StringProperty = "StringValue1";            
            modelD2.ModelCCollection1.Add(childModel);
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            modelD1.CommitGraph();
            modelD2.CommitGraph();

            modelD1.ModelCCollection1.Add(factory.CreateModel<ModelD>());
            childModel = factory.CreateModel<ModelD>();
            childModel.GuidProperty1 = Guid.NewGuid();
            modelD2.ModelCCollection1.Add(childModel);
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            modelD1.ModelDProperty = factory.CreateModel<ModelD>();
            modelD1.ModelDProperty.IntProperty1 = 34298;
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);

            modelD1.ModelDProperty = factory.CreateModel<ModelD>();
            modelD1.ModelDProperty.IntProperty1 = 123;
            modelD2.ModelDProperty = factory.CreateModel<ModelD>();
            modelD2.ModelDProperty.IntProperty1 = 456;
            modelD1.Merge(modelD2);
            AssertValuesAreTheSame(modelD1, modelD2);
        }

        [TestMethod]
        public void TestMergeWithDeepObjectGraph()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;

            ModelC modelC1 = factory.CreateModel<ModelC>();
            modelC1.FirstName = "Jane";
            modelC1.LastName = "Doe";
            modelC1.IntProperty = 123;
            modelC1.CharProperty = 'E';
            modelC1.DecimalProperty = 456M;
            modelC1.GuidProperty = Guid.Empty;
            
            ModelD modelD1 = factory.CreateModel<ModelD>();
            modelD1.IntProperty1 = 123;
            modelD1.CharProperty1 = 'E';
            modelD1.DecimalProperty1 = 456M;
            modelD1.GuidProperty1 = Guid.Empty;

            modelD1.ModelDCollection.Add(factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true));
            modelD1.ModelDCollection[0].ModelDCollection.Add(factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true));
            modelD1.ModelDCollection[0].ModelDCollection1.Add(factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true));
            modelD1.ModelDCollection[0].ModelCCollection1.Add(factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true));

            modelD1.ModelDCollection.Add(factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true));
            modelD1.ModelDCollection[1].ModelDCollection.Add(factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true));
            modelD1.ModelDCollection[1].ModelDCollection[0].ModelCProperty = factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true);
            modelD1.ModelDCollection[1].ModelDCollection[0].ModelDProperty = factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true);

            modelC1.ModelCCollection.Add(factory.CreateDeepCopy(modelC1, GetTypesForSerialization(), true));
            modelC1.ModelCCollection[0].ModelCProperty = factory.CreateDeepCopy(modelC1, GetTypesForSerialization(), true);
            modelC1.ModelCCollection[0].ModelCProperty.ModelCProperty = factory.CreateDeepCopy(modelC1, GetTypesForSerialization(), true);
            modelC1.ModelCCollection[0].ModelCCollection.Add(factory.CreateDeepCopy(modelD1, GetTypesForSerialization(), true));
            modelC1.ModelCCollection[0].ModelCCollection.Add(factory.CreateDeepCopy(modelC1, GetTypesForSerialization(), true));

            modelC1.ModelCCollection.Add(factory.CreateDeepCopy(modelC1, GetTypesForSerialization(), true));
            modelC1.ModelCCollection[1].ModelCCollection.Add(factory.CreateDeepCopy(modelD1, GetTypesForSerialization(), true));
            modelC1.ModelCCollection[1].ModelCCollection[0].ModelDProperty = factory.CreateDeepCopy(modelD1, GetTypesForSerialization(), true);

            modelC1.CommitGraph();
            ModelC modelC2 = factory.CreateDeepCopy(modelC1, GetTypesForSerialization());

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC1.Merge(modelC2);

            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsFalse(modelC2.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC2.ModelCCollection[0].ModelCCollection[0].ModelDCollection[0].StringProperty = "StringValueA";
            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsFalse(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsTrue(modelC2.IsDirtyWithin);

            modelC1.Merge(modelC2);
            Assert.IsFalse(modelC1.IsDirty);
            Assert.IsTrue(modelC1.IsDirtyWithin);
            Assert.IsFalse(modelC2.IsDirty);
            Assert.IsTrue(modelC2.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);

            modelC1.CommitGraph();
            modelC2.CommitGraph();

            modelC2.ModelCCollection.RemoveAt(0);
            modelC2.ModelCCollection[0].ModelCCollection[0].ModelDProperty = factory.CreateDeepCopy<ModelD>(modelD1, GetTypesForSerialization(), true);
            modelC1.Merge(modelC2);

            Assert.IsTrue(modelC1.IsDirty);
            Assert.IsTrue(modelC1.IsDirtyWithin);
            Assert.IsTrue(modelC2.IsDirty);
            Assert.IsTrue(modelC2.IsDirtyWithin);
            AssertValuesAreTheSame(modelC1, modelC2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestNullUniqueIdInSourceForProperty()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD1.ModelDProperty1 = factory.CreateModel<ModelD>();
            modelD1.ModelDProperty1.UniqueId = null;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestEmptyUniqueIdInSourceForProperty()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD1.ModelDProperty1 = factory.CreateModel<ModelD>();
            modelD1.ModelDProperty1.UniqueId = Guid.Empty;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestNullUniqueIdInTargetForProperty()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD2.ModelDProperty1 = factory.CreateModel<ModelD>();
            modelD2.ModelDProperty1.UniqueId = null;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestEmptyUniqueIdInTargetForProperty()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD2.ModelDProperty1 = factory.CreateModel<ModelD>();
            modelD2.ModelDProperty1.UniqueId = Guid.Empty;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestNullUniqueIdInSourceForCollection()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD1.ModelDCollection1.Add(factory.CreateModel<ModelD>());
            modelD1.ModelDCollection1[0].UniqueId = null;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestEmptyUniqueIdInSourceForCollection()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD1.ModelDCollection1.Add(factory.CreateModel<ModelD>());
            modelD1.ModelDCollection1[0].UniqueId = Guid.Empty;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestNullUniqueIdInTargetForCollection()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD2.ModelDCollection1.Add(factory.CreateModel<ModelD>());
            modelD2.ModelDCollection1[0].UniqueId = null;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestEmptyUniqueIdInTargetForCollection()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelD modelD1 = factory.CreateModel<ModelD>();
            ModelD modelD2 = factory.CreateModel<ModelD>();

            modelD2.ModelDCollection1.Add(factory.CreateModel<ModelD>());
            modelD2.ModelDCollection1[0].UniqueId = Guid.Empty;
            modelD1.Merge(modelD2);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestNullMerge()
        {
            ModelFactory factory = new ModelFactory();
            ModelA modelA = factory.CreateModel<ModelA>();
            modelA.Merge<ModelA, ModelA>(null);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestDirectlyMergingDerivedType()
        {
            ModelFactory factory = new ModelFactory();
            factory.AssignModelUniqueId = true;
            ModelC modelC1 = factory.CreateModel<ModelC>();
            ModelD modelD1 = factory.CreateModel<ModelD>();
            modelC1.Merge(modelD1);
        }

        private void AssertValuesAreTheSame(ModelC model1, ModelC model2)
        {
            Assert.IsNotNull(model2);
            Assert.AreEqual(model1.FirstName, model2.FirstName);
            Assert.AreEqual(model1.LastName, model2.LastName);
            Assert.AreEqual(model1.FirstAndLastName.Value, model2.FirstAndLastName.Value);
            Assert.AreEqual(model1.IntProperty, model2.IntProperty);
            Assert.AreEqual(model1.CharProperty, model2.CharProperty);
            Assert.AreEqual(model1.DecimalProperty, model2.DecimalProperty);
            Assert.AreEqual(model1.NullableIntProperty, model2.NullableIntProperty);
            Assert.AreEqual(model1.NullableCharProperty, model2.NullableCharProperty);
            Assert.AreEqual(model1.NullableDecimalProperty, model2.NullableDecimalProperty);
            Assert.AreEqual(model1.ObjectProperty, model2.ObjectProperty);
            Assert.AreEqual(model1.StringProperty, model2.StringProperty);
            Assert.AreEqual(model1.GuidProperty, model2.GuidProperty);
            Assert.AreEqual(model1.NullableGuidProperty, model2.NullableGuidProperty);
            Assert.AreEqual(model1.TestEnumProperty, model2.TestEnumProperty);
            Assert.AreEqual(model1.NullableTestEnumProperty, model2.NullableTestEnumProperty);

            if (model1.ModelCProperty == null)
            {
                Assert.IsNull(model2.ModelCProperty);
            }
            else
            {
                if (model1.ModelCProperty is ModelD)
                {
                    AssertValuesAreTheSame(model1.ModelCProperty as ModelD, model2.ModelCProperty as ModelD);
                }
                else
                {
                    AssertValuesAreTheSame(model1.ModelCProperty, model2.ModelCProperty);
                }
            }

            if (model1.ModelDProperty == null)
            {
                Assert.IsNull(model2.ModelDProperty);
            }
            else
            {
                AssertValuesAreTheSame(model1.ModelDProperty, model2.ModelDProperty);
            }

            Assert.AreEqual(model1.ModelCCollection.Count, model2.ModelCCollection.Count);

            foreach (ModelC model1Child in model1.ModelCCollection)
            {
                ModelC model2Child = model2.ModelCCollection.Single<ModelC>(m => m.UniqueId == model1Child.UniqueId);
                AssertValuesAreTheSame(model1Child, model2Child);
            }

            Assert.AreEqual(model1.ModelDCollection.Count, model2.ModelDCollection.Count);

            foreach (ModelC model1Child in model1.ModelDCollection)
            {
                ModelC model2Child = model2.ModelDCollection.Single<ModelC>(m => m.UniqueId == model1Child.UniqueId);
                AssertValuesAreTheSame(model1Child, model2Child);
            }
        }

        private void AssertValuesAreTheSame(ModelD model1, ModelD model2)
        {
            AssertValuesAreTheSame(model1 as ModelC, model2 as ModelC);
            Assert.AreEqual(model1.IntProperty1, model2.IntProperty1);
            Assert.AreEqual(model1.CharProperty1, model2.CharProperty1);
            Assert.AreEqual(model1.DecimalProperty1, model2.DecimalProperty1);
            Assert.AreEqual(model1.NullableIntProperty1, model2.NullableIntProperty1);
            Assert.AreEqual(model1.NullableCharProperty1, model2.NullableCharProperty1);
            Assert.AreEqual(model1.ObjectProperty1, model2.ObjectProperty1);
            Assert.AreEqual(model1.StringProperty1, model2.StringProperty1);
            Assert.AreEqual(model1.GuidProperty1, model2.GuidProperty1);
            Assert.AreEqual(model1.NullableGuidProperty1, model2.NullableGuidProperty1);
            Assert.AreEqual(model1.TestEnumProperty1, model2.TestEnumProperty1);
            Assert.AreEqual(model1.NullableTestEnumProperty1, model2.NullableTestEnumProperty1);

            if (model1.ModelCProperty1 == null)
            {
                Assert.IsNull(model2.ModelCProperty1);
            }
            else
            {
                if (model1.ModelCProperty1 is ModelD)
                {
                    AssertValuesAreTheSame(model1.ModelCProperty1 as ModelD, model2.ModelCProperty1 as ModelD);
                }
                else
                {
                    AssertValuesAreTheSame(model1.ModelCProperty1, model2.ModelCProperty1);
                }
            }

            if (model1.ModelDProperty1 == null)
            {
                Assert.IsNull(model2.ModelDProperty1);
            }
            else
            {
                AssertValuesAreTheSame(model1.ModelDProperty1, model2.ModelDProperty1);
            }

            Assert.AreEqual(model1.ModelCCollection1.Count, model2.ModelCCollection1.Count);

            foreach (ModelC model1Child in model1.ModelCCollection1)
            {
                ModelC model2Child = model2.ModelCCollection1.Single<ModelC>(m => m.UniqueId == model1Child.UniqueId);
                AssertValuesAreTheSame(model1Child, model2Child);
            }

            Assert.AreEqual(model1.ModelDCollection1.Count, model2.ModelDCollection1.Count);

            foreach (ModelC model1Child in model1.ModelDCollection1)
            {
                ModelC model2Child = model2.ModelDCollection1.Single<ModelC>(m => m.UniqueId == model1Child.UniqueId);
                AssertValuesAreTheSame(model1Child, model2Child);
            }
        }

        private List<Type> GetTypesForSerialization()
        {
            return new List<Type>()
            {
                typeof(TestEnum)
            };
        }
    }
}
